ISD数据集:分析全球120年气候变化
ISD 是 Intergrated Surface Dataset 的缩写,是 NOAA 美国国家海洋和大气管理局公开的一份数据集。包括了全球接近3万个地表气象站从 1900 年迄今的观测记录,气象领域的朋友对此应该非常熟悉。
我最近重新整理了一下这份数据集:编写了下载的脚本,解析的Parser,建模的PostgreSQL DDL,查询的SQL语句,可视化的Grafana Dashboard,以及清理好的 CSV 原始数据。用于探索分析,教学演示与数据库性能测试对比。
公开 Demo:http://demo.pigsty.cc/d/isd-overview
项目的地址是:https://github.com/Vonng/isd
动机
ISD 可以用来探索分析,或者测试衡量数据库性能。但更重要的是,提供了一个绝佳的学习场景。在《为什么要学数据库原理?》一文中,我提到过学习数据库最好的方式就是动起手来做点东西。ISD 就是一个极好的演示样例:
使用 Go 下载、解析、录入最新的原始数据。
使用 PostgreSQL 建模,存储,分析数据。
使用 Grafana 读取,呈现,可视化数据。
麻雀虽小,但是五脏俱全,三者配合实现了一个可以查询所有气象站历史气象要素的小应用。用户可以交互式地探索,也可以自动更新最新数据。更重要的是,它足够简单,可以方便地演示一个数据应用到底是如何运作起来的。
数据存储与建模
ISD 提供了四种粒度的数据集:亚小时级原始观测数据(hourly),每日统计摘要数据(daily),月度统计摘要数据(monthly),年度统计摘要数据(yearly),每一级都是由上一级按时间维度聚合而成。
其中最为重要的是前两者:isd.hourly 是气象站的原始观测记录,保留着最丰富的信息。isd.daily 是天级别的聚合汇总摘要,可以用来生成月度与年度的汇总摘要。
在本项目中,默认使用了 isd.daily 数据,清洗压缩后约 2.8GB , 1.6亿条。灌入 PostgreSQL 展开后含索引大概 30GB, 具体格式如下:
CREATE TABLE isd.daily
(
-- 主键
station VARCHAR(12) NOT NULL, -- 台站号 6USAF+5WBAN
ts DATE NOT NULL, -- 观测日期
-- 温湿度
temp_mean NUMERIC(3, 1), -- 平均温度 (℃)
temp_min NUMERIC(3, 1), -- 最低温度 ℃
temp_max NUMERIC(3, 1), -- 最高温度 ℃
dewp_mean NUMERIC(3, 1), -- 平均露点 (℃)
-- 气压
slp_mean NUMERIC(5, 1), -- 海平面气压 (hPa)
stp_mean NUMERIC(5, 1), -- 站点气压 (hPa)
-- 视距
vis_mean NUMERIC(6), -- 可视距离 (m)
-- 风速
wdsp_mean NUMERIC(4, 1), -- 平均风速 (m/s)
wdsp_max NUMERIC(4, 1), -- 最大风速 (m/s)
gust NUMERIC(4, 1), -- 最大阵风 (m/s)
-- 降水/雪
prcp_mean NUMERIC(5, 1), -- 降水量 (mm)
prcp NUMERIC(5, 1), -- 根据降水标记修正后的降水量 (mm)
sndp NuMERIC(5, 1), -- 当日最新上报的雪深 (mm)
-- 天气现象 FRSHTT (Fog/Rain/Snow/Hail/Thunder/Tornado)
is_foggy BOOLEAN, -- 雾 (F)og
is_rainy BOOLEAN, -- 雨 (R)ain or Drizzle
is_snowy BOOLEAN, -- 雪 (S)now or pellets
is_hail BOOLEAN, -- 雹 (H)ail
is_thunder BOOLEAN, -- 雷 (T)hunder
is_tornado BOOLEAN, -- 龙卷 (T)ornado or Funnel Cloud
-- 计算各个统计量所使用的观测记录数量
temp_count SMALLINT, -- 用于计算温度统计量的记录数量
dewp_count SMALLINT, -- 用于计算平均露点的记录数量
slp_count SMALLINT, -- 用于计算海平面气压统计量的记录数量
stp_count SMALLINT, -- 用于计算站点气压统计量的记录数量
wdsp_count SMALLINT, -- 用于计算风速统计量的记录数量
visib_count SMALLINT, -- 用于计算视距的记录数量
-- 辅助标记
temp_min_f BOOLEAN, -- 最低温度是统计得出(而非直接上报)
temp_max_f BOOLEAN, -- 同上,最高温度
prcp_flag CHAR, -- 降水量标记: ABCDEFGHI
PRIMARY KEY (station, ts)
);
当然,还有一些关于气象站的元数据,以辅助表的形式存在:isd.station 存储了气象站基本信息,标号,名称,国家,位置,海拔,服役时间等。isd.history 存储了按月统计的历史观测记录数,isd.world 存储了世界上国家/地区的详细信息与地理边界(来自欧盟统计部门),isd.china 存储了中国行政区划信息,isd.mwcode 存储了天气代码的具体解释条目,isd.element 存储了气象要素的说明与数据覆盖率。
数据获取与解析
除了辅助表,字典表这些,其他的数据需要从 NOAA 上下载。这里,我提供了一系列的包装脚本,您可以直接使用简单的命令完成配置,特别是:如果您在使用 Pigsty —— 开箱即用的 PostgreSQL 数据库发行版,单机安装时已经为您配置好了 PostgreSQL 与 Grafana ,只需要 make all ,就可以完成所有的配置工作。
在原始的 Daily 数据集中,有极少量的重复数据与脏数据,我已经进行了清洗处理,您可以选择我们已经解析清理好的 CSV 数据集直接导入。如果需要获取本年度最近几天的更新,您可以选择选择使用 Go Parser 直接从 NOAA 网站下载原始数据并解析。
数据解析器使用 Go 语言编写,您可以直接编译,或者直接下载编译好的二进制文件。解析器以管道模式工作,将年度数据 tarball 喂给它,它就会自动输出解析好的 CSV 数据。可以直接被 PostgreSQL COPY 命令消化。
数据分析与可视化
存储在 PostgreSQL 中的数据可以通过 Grafana 访问并进行可视化。例如,下面的 ISD Station 面板就会展示出一个具体气象站(拉萨)的详细信息,观测摘要,原始数据,以及气象要素可视化。
在元数据部分,会列出气象站的基本信息,编号,名称,国家,位置,服役时间,在地图上标出位置,并列出周边最近的气象站及其距离,点击即可前往相邻气象站的详情页面。
在摘要部分,会给出该气象站的月度观测计数,历史极值记录,年度数据将汇总,以及月度统计摘要。包括气温,湿度,降水,风速,天气等核心指标。而下面的气象要素部分,就会给出所选时间段的图表。
在摘要部分,会给出该气象站的月度观测计数,历史极值记录,年度数据将汇总,以及月度统计摘要。包括气温,湿度,降水,风速,天气等核心指标。而下面的气象要素部分,就会给出所选时间段的图表。
如果您对更精细粒度数据感兴趣,点击月份导航,会自动跳转到 ISD Detail 面板中,这里会提供日汇总级别的摘要数据,以及亚小时级别的原始观测记录。此外,在气象要素部分,也会展示一些额外的指标,包括分钟级别的温度,露点,气压,风速,风向,云量,可见度,降水,降雪,以及其他天气情况代码。
其他用途
很多数据库性能评测都使用抽象的案例,随机的数据生成器。而本项目提供了一个有用且“真实”的实际场景,用于衡量数据库的性能。
举个例子,观测数据属于很典型的 时序数据,那么我们就可以用它来考察 TimescaleDB 或者其他 时序数据库在这个场景下的性能表现。比如,同样的数据,使用 PostgresQL 默认的堆表与索引一共是 29 GB,但是使用 TimescaleDB 扩展压缩后,压缩到 15% —— 4.6GB。这个压缩率还是很可以的,因为 gzip --best 处理原始排序 CSV 也就能做到 2.8 GB 。关键是压缩也没影响查询速度:在我的 Apple M1 Max 笔记本上,原本全表算 count/min/max/avg 大约需要 12 秒,压缩后只需要 4.4 秒了。 更为极端的场景,可以使用 1TB 的 isd.hourly 数据集进行。
当然,数据集怎么用,还是取决于用户。比如,你可以问问 GPT,基于这份数据,能不能得出全球气温在变暖的结论呢?